#include <map>
#include <iostream>
#include "Poco/Logger.h"
#include "Poco/Message.h"
#include "Poco/Channel.h"
#include "Poco/FileChannel.h"
#include "Poco/ConsoleChannel.h"
#include "Poco/FormattingChannel.h"
#include "Poco/PatternFormatter.h"
#include "Poco/Exception.h"
#include "Ini/Ini.h"
#include "Directory/Directory.h"
#include "PocoLogger.h"

using namespace SystemTool;

// Construct logger
PocoLogger* PocoLogger::GetInstance()
{
    static PocoLogger LogInstance;
    return &LogInstance;
}

// Construct the obj
PocoLogger::PocoLogger()
{
    m_pbIsInited = new std::atomic<bool>;
}

// Destruct the obj
PocoLogger::~PocoLogger()
{
    if(m_pbIsInited)
    {
        delete m_pbIsInited;
        m_pbIsInited = nullptr;
    }
}

// Init log cfg
void PocoLogger::InitCfg()
{
    if (!(m_pbIsInited->load()))
    {
        std::string strCfgPath = SystemTool::Directory::GetAppDataPath() + "/LogCfg.ini";

        int iRotationSize = 10;
        if (!SystemTool::Ini::GetKey(strCfgPath, "Log", "RotationSize", iRotationSize))
        {
            SystemTool::Ini::SetKey(strCfgPath, "Log", "RotationSize", iRotationSize);
        }
        m_iRotationSize = iRotationSize;

        int iCutoffDays = 7;
        if (!SystemTool::Ini::GetKey(strCfgPath, "Log", "PurgeDay", iCutoffDays))
        {
            SystemTool::Ini::SetKey(strCfgPath, "Log", "PurgeDay", iCutoffDays);
        }
        m_iCutoffDays = iCutoffDays;

        int iCutoffCnt = 7;
        if (!SystemTool::Ini::GetKey(strCfgPath, "Log", "PurgeCnt", iCutoffCnt))
        {
            SystemTool::Ini::SetKey(strCfgPath, "Log", "PurgeCnt", iCutoffCnt);
        }
        m_iCutoffCnt = iCutoffCnt;

        bool bIsOpenDebugLog = false;
        if (!SystemTool::Ini::GetKey(strCfgPath, "Log", "IsEnableDebugLog", bIsOpenDebugLog))
        {
            SystemTool::Ini::SetKey(strCfgPath, "Log", "IsEnableDebugLog", bIsOpenDebugLog);
        }
        m_bIsOpenDebugLog = bIsOpenDebugLog;
        m_pbIsInited->store(true);
    }
}

// Add the logger
void PocoLogger::AddFileLogger(std::string strModuleName, std::string strLogFilePath)
{
    if(strLogFilePath.empty())
    {
        return;
    }

    InitCfg();

    try
    {
        // Create Pattern
        Poco::PatternFormatter::Ptr pPattern = new Poco::PatternFormatter("[%Y-%m-%d %H:%M:%S][%P][%J][%q][%O][%u][%t]");

        // Create file channel
        Poco::Channel::Ptr pFileChannel = new Poco::FileChannel(strLogFilePath);
        std::string strRotationSize = std::to_string(m_iRotationSize) + "M";
        pFileChannel->setProperty(Poco::FileChannel::PROP_ROTATION, strRotationSize);
        pFileChannel->setProperty(Poco::FileChannel::PROP_ARCHIVE, "timestamp");
        pFileChannel->setProperty(Poco::FileChannel::PROP_TIMES, "local");
        std::string strCutoffDays = std::to_string(m_iCutoffDays) + "days";
        pFileChannel->setProperty(Poco::FileChannel::PROP_PURGEAGE, strCutoffDays);
        std::string strCutoffCnt = std::to_string(m_iCutoffCnt);
        pFileChannel->setProperty(Poco::FileChannel::PROP_PURGECOUNT, strCutoffCnt);
        pFileChannel->setProperty(Poco::FileChannel::PROP_FLUSH, "true");

        // Bind file channel with pattern
        Poco::FormattingChannel::Ptr pFormatChannel = new Poco::FormattingChannel(pPattern, pFileChannel);

        if (m_bIsOpenDebugLog)
        {
            // Create logger
            Poco::Logger::create(strModuleName, pFormatChannel, 7);
        }
        else
        {
            // Create logger
            Poco::Logger::create(strModuleName, pFormatChannel, 6);
        }
    }
    catch (Poco::Exception &e)
    {
        std::cout << "Err happened:" << e.displayText() << std::endl;
    }
}

// Add the logger
void PocoLogger::AddConsoleLogger(std::string strModuleName)
{
    try
    {
        Poco::PatternFormatter::Ptr pPattern = new Poco::PatternFormatter("[%Y-%m-%d %H:%M:%S][%P][%J][%q][%O][%u][%t]");
        Poco::Channel::Ptr pConsoleChannel = new Poco::ConsoleChannel(std::cout);
        Poco::FormattingChannel::Ptr pFormatChannel = new Poco::FormattingChannel(pPattern, pConsoleChannel);
        Poco::Logger::create(strModuleName, pFormatChannel, 7);
    }
    catch (Poco::Exception &e)
    {
        std::cout << "Err happened:" << e.displayText() << std::endl;
    }
}

// Log the msg
void PocoLogger::Log(std::string strModuleName,
                 String strMsg,
                 std::string strFuncFilePath,
                 int iLineNo,
                 int iLevel)
{
    try
    {
        // Log msg to file
        Poco::Logger &CurLogger = Poco::Logger::get(strModuleName);
        if (iLevel == 3 /*Error*/)
        {
            CurLogger.error(strMsg.Data(), strFuncFilePath.c_str(), iLineNo);
        }
        else if (iLevel == 4 /*Warn*/)
        {
            CurLogger.warning(strMsg.Data(), strFuncFilePath.c_str(), iLineNo);
        }
        else if (iLevel == 6 /*info*/)
        {
            CurLogger.information(strMsg.Data(), strFuncFilePath.c_str(), iLineNo);
        }
        else if (iLevel == 7 /*debug*/)
        {
            CurLogger.debug(strMsg.Data(), strFuncFilePath.c_str(), iLineNo);
        }
    }
    catch (Poco::Exception &e)
    {
        std::cout << "Err happened:" << e.displayText() << std::endl;
    }
}
